import { PipeTransform, Injectable, ArgumentMetadata } from '@nestjs/common';
import { validate } from 'class-validator';
import { plainToClass } from 'class-transformer';
import { UserInputError } from 'apollo-server-express';

type TMetatype =
  | StringConstructor
  | BooleanConstructor
  | NumberConstructor
  | ArrayConstructor
  | ObjectConstructor;

@Injectable()
export class GraphQLValidationPipe<T> implements PipeTransform<T> {
  public async transform(value: T, { metatype }: ArgumentMetadata) {
    if (!metatype || !this.toValidate(metatype as TMetatype)) {
      return value;
    }
    const object = plainToClass(metatype, value);
    const errors = await validate(object);
    if (errors.length > 0) {
      const message = errors
        .map((validationError) => {
          if (validationError?.constraints) {
            // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
            const constraints = validationError.constraints!;
            Object.values(constraints);
          }
          return null;
        })
        .filter(Boolean)
        .flat()
        .join('; ');
      throw new UserInputError(message);
    }
    return value;
  }
  private toValidate(metatype: TMetatype): boolean {
    const types = [String, Boolean, Number, Array, Object];
    return !types.includes(metatype);
  }
}
